#ifndef ARRAY_H
#define ARRAY_H

#include <iostream>
#include <vector>
#include <string>

#include "Value.h"

using namespace std;

class ArrayException: public std::exception
{
public:
	ArrayException(std::string s) : msg(s) {}
	ArrayException() throw() {}
	virtual ~ArrayException() throw() {}
	virtual const char* what() const throw()
	{
		return msg.c_str();
	}
	std::string msg;
};

template< typename T >
class Array
{
public:
	explicit Array(int n=0) throw() : type(type_of<T>::object), size(n), elements(new T[n]) {}
	virtual ~Array() throw() { delete[] elements; }

	T const& operator[] (int index) const 	throw() { return elements[check(index)]; }
	T& 	 operator[] (int index) 	throw() { return elements[check(index)]; }

	int push(const T& v)
	{
		T* tmp = elements;
		elements = new T[size+1];
		assert(elements!=0);
		for(int i=0; i<size; ++i)
			elements[i] = tmp[i];
		elements[size] = v;
		size++;
		delete[] tmp;

		return size;
	}

	T& pop()
	{
//		T back = data.back();
//		data.pop_back();
//		return back;
		return elements[size];
	}
	
	void shift()
	{
		T* tmp = elements;
		//data[size].~T();
		delete elements[size];
	}

	void unshift(const T& v)
	{
		T* tmp = elements;
		elements = new T[size+1];
		assert(elements!=0);
		for(int i=0; i<size; ++i)
			elements[i+1] = tmp[i];
		size++;
		elements[0] = v;
		delete[] tmp;		
	}

	inline bool empty() const { return elements==0 && size==0; }

//	std::string const& toString()
	std::string toString()
	{
		std::stringstream ss;
		ss << "[";

//		for(DataVectorIterator it = data.begin(); it != data.end(); ++it)
//			{
//				ss << *it;
//			}

		for(int i=0; i<size; i++)
			ss <<  elements[i] << (i == (size-1) ? "" : ", ");
		ss << "]";
		return ss.str();
	}

//	std::string const& toString2(int indent=1)
	std::string toString2(int indent=1)
	{
		std::stringstream ss;
		ss << "Array = [" << std::endl;
//		for(int i=0; i<size(); ++i)
//			{
//				for(int j=0; j<indent; j++,	ss << '\t');
//				ss <<  data[i] << (i == (size()-1) ? "" : ", ") << std::endl;
//			}

		ss << "]";
		return ss.str();
	}

	ValueType const& getType() { return type; }
//	ValueType const& getType() { return type_of<T>::object; }

	unsigned int size;
private:
	int check(int i) const
	{
		if(i<0 || i>=size)
			throw ArrayException("Array index out of bounds");
		return i;
	}
	
protected:
	ValueType 	type;
	T*	elements;
};

#define ValueArray ::Array< ::Value >

#endif
